#include "LoginCon.h"
#include "LoginSvr.h"
#include <sys/time.h>
#include "Game.pb.h"
#include "encrypt.h"
#include "LoginAcc.h"

using namespace std;
using namespace proto;
using namespace lc;

LoginCon::LoginCon()
{
}

LoginCon::~LoginCon()
{
}

bool LoginCon::SendMsg(uint32 cmd, const std::string& msg)
{
	string s;
	s.append((char *)&cmd, sizeof(cmd));
	s.append(msg.c_str(), msg.length());
	return SendPack(s.c_str(), s.length());
}

void LoginCon::OnRecv(const lc::MsgPack &msg)
{	
	L_COND_V(msg.len >= sizeof(uint32));
	uint32 cmd = 0;
	const char* cur = msg.data;
	cmd = *(decltype(cmd)*)(cur);
	cur += sizeof(cmd);
	std::string str(cur, msg.len - sizeof(cmd));

	if(!gLoginSvr.m_readySet.all())
	{
		LERROR("login not ready");
		return;
	}
	
	switch (cmd)
	{
	default:
		L_ERROR("unknow cmd %x", cmd);
		break;
	case LOGIN_CMD:
		ReqLogin(cmd, str);
		break;
	}
}

static void DoLoginOk(LoginCon *pClient, string name, uint64 cid)
{
	time_t now = gSysTime.Sec();
	string src = name + StrNum::NumToStr(now);
	string signature;
	Encrypt::encryptByHmacSha1(HMAC_SHA_KEY, src, signature);
	LDEBUG("acc,ts,signature=", name, now, signature);
	
	// 临时固定acc地址，以后再扩展负载均衡功能
	LoginAccRes res;
	auto acc = gLoginSvr.GetBlanceAcc();
	res.set_acc_ip(acc.ip);
	res.set_acc_port(acc.port);
	res.set_isok(true);
	LoginToken* token = res.mutable_token(); 
	token->set_name(name);
	token->set_timestamp(now);
	token->set_signature(signature);
	LDEBUG("send len=", res.SerializeAsString().length(), "data=", res.SerializeAsString());
	pClient->SendMsg(LOGIN_CMD, res.SerializeAsString());
}

void LoginCon::ReqLogin(int32 cmd, const string &msg)
{
	LoginAccReq req;
	L_COND_V (req.ParseFromString(msg));
	L_COND_V(!req.psw().empty());
	
	auto opt = [req, cid = GetId()](LoginAcc& acc)
	{
		LoginCon *pClient = dynamic_cast<LoginCon*>(gLoginSvr.m_clientListener.GetConnMgr().FindConn(cid));
		L_COND_V(pClient, "miss connect after get acc from db");
		if (req.psw() != acc.m_dbAcc.psw)
		{
			LDEBUG("login acc psw wrong");
			LoginAccRes res;
			res.set_isok(false);
			pClient->SendMsg(LOGIN_CMD, res.SerializeAsString());
			return;
		}
		
		LDEBUG("login acc ok");
		acc.OnCon();
		DoLoginOk(pClient, req.name(), cid);
	};

	auto optFail = [req, cid = GetId()](string name)
	{
		//首次登录直接注册
		DbAcc dbAcc;
		dbAcc.name = name;
		dbAcc.psw = req.psw();
		LDEBUG("login acc not exist. create new acc", dbAcc.name);

		Dbproxy::Ins().Insert(dbAcc, [name, cid](bool isok)
		{
			LoginCon *pClient = dynamic_cast<LoginCon*>(gLoginSvr.m_clientListener.GetConnMgr().FindConn(cid));
			L_COND_V(pClient, "miss connect after get acc from db");
			if (!isok)
			{
				LWARN("create acc fail. ", name);//查找name失败，再插入还失败？ 很低概率可能多人同时创建账号
				LoginAccRes res;
				res.set_isok(false);
				pClient->SendMsg(LOGIN_CMD, res.SerializeAsString());
				return;
			}

			LDEBUG("create new acc on db ok");
			DoLoginOk(pClient, name, cid);
		});
	};
	LoginAccOptMgr::Ins().AddOpt(req.name(), opt, optFail);
}






